﻿using System;
using System.Collections;
using System.Linq;
using System.Text;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Json
{
    /// <summary>
    /// json序列化中心-序列化
    /// </summary>
    public partial class JsonSerializerCore
    {
        /************************************************************************
         * 引用类型为null时,值为null
         * 
         * char             "a"
         * char?            null
         * string           "abc"
         * byte             11
         * sbyte            -11
         * uint16           12
         * int16            -12
         * uint32           13
         * int32            -13
         * uint64           14
         * int64            -14
         * float            15.65
         * double           16.789
         * decimal          17.56
         * bool             true
         * DateTime         "2000-04-05T12:25:36"
         * DateTimeOffset   "2000-04-06T12:25:36+08:00"
         * DateOnly         "2000-04-06"    
         * TimeOnly         "12:25:36"
         * 
         * 
         * 实现IList接口的类型
         * 不为null空集合
         * []
         * 
         * 有元素的集合
         * [
         *  {"key1":"value1","key2":"value2",..},
         *  {"key1":"value1","key2":"value2",..},
         * ]
         * 
         * 实现IDictionary接口的类型
         * 不为null空集合
         * {}
         * 
         * 有元素的集合
         * {
         *  "key1":"value1",
         *  "key2":"value2",
         *  ...
         * }
        ************************************************************************/

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public string Serialize(object obj)
        {
            Type type = obj.GetType();
            JsonNode jsonNode = this.GetJsonNode(type);
            StringBuilder sb = new StringBuilder();

            switch (jsonNode.TypeCategory)
            {
                case TypeCategorys.Basic:
                    if (obj == null)
                    {
                        return JsonConstants.NULL;
                    }
                    else
                    {
                        IJsonValueConverter converter = JsonConverterManger.GetDefaultConverter(type);
                        string jsonValue;
                        if (converter.TryConvertTo(null, type, null, obj, out jsonValue))
                        {
                            return jsonValue;
                        }
                        else
                        {
                            throw new ArgumentException($"类型{jsonNode.Type.DeclaringType.FullName}中的值\"{obj}\"转换为json值失败");
                        }
                    }
                case TypeCategorys.Object:
                    this.WriteJsonObject(sb, jsonNode, obj);
                    break;
                case TypeCategorys.IList:
                    this.WriteJsonIList(sb, jsonNode, obj);
                    break;
                case TypeCategorys.IDictionary:
                    this.WriteJsonIDictionary(sb, jsonNode, obj);
                    break;
                default:
                    throw new NotImplementedException($"json序列中未实现的数据类型{jsonNode.TypeCategory.ToString()}");
            }

            return sb.ToString();
        }

        private void WriteJsonIDictionary(StringBuilder sb, JsonNode jsonNode, object obj)
        {
            sb.Append(JsonConstants.LEFT_BRACE);
            IDictionary dic = (IDictionary)obj;
            JsonNode jsonNode2 = jsonNode.Children.First();
            object value;
            string jsonValue;
            bool addComma = false;

            switch (jsonNode2.TypeCategory)
            {
                case TypeCategorys.Basic:
                case TypeCategorys.Any:
                    foreach (var key in dic.Keys)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        value = dic[key];
                        if (jsonNode2.ValueConverter.TryConvertTo(jsonNode.ObjectMemberInfo, jsonNode2.Type, obj, value, out jsonValue))
                        {
                            this.WriteBasic(sb, key.ToString(), jsonValue, jsonNode2.ValueConverter.HasAddDoubleQuotes);
                        }
                        else
                        {
                            throw new ArgumentException($"类型{jsonNode2.Type.DeclaringType.FullName}中的值\"{value}\"转换为json值失败");
                        }
                        addComma = true;
                    }
                    break;
                case TypeCategorys.Object:
                    foreach (var key in dic.Keys)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        //名称
                        sb.Append(JsonConstants.DOUBLE_QUOTES);
                        sb.Append(key.ToString());
                        sb.Append(JsonConstants.DOUBLE_QUOTES);

                        //冒号
                        sb.Append(JsonConstants.COLON_MARK);

                        value = dic[key];
                        this.WriteJsonObject(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                case TypeCategorys.IList:
                    foreach (var key in dic.Keys)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        //名称
                        sb.Append(JsonConstants.DOUBLE_QUOTES);
                        sb.Append(key.ToString());
                        sb.Append(JsonConstants.DOUBLE_QUOTES);

                        //冒号
                        sb.Append(JsonConstants.COLON_MARK);

                        value = dic[key];
                        this.WriteJsonIList(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                case TypeCategorys.IDictionary:
                    foreach (var key in dic.Keys)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        //名称
                        sb.Append(JsonConstants.DOUBLE_QUOTES);
                        sb.Append(key.ToString());
                        sb.Append(JsonConstants.DOUBLE_QUOTES);

                        //冒号
                        sb.Append(JsonConstants.COLON_MARK);

                        value = dic[key];
                        this.WriteJsonIDictionary(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                default:
                    throw new NotImplementedException(jsonNode2.TypeCategory.ToString());
            }

            sb.Append(JsonConstants.RIGHT_BRACE);
        }

        private void WriteJsonIList(StringBuilder sb, JsonNode jsonNode, object obj)
        {
            sb.Append(JsonConstants.LEFT_BRCKET);
            IList list = (IList)obj;
            string jsonValue;
            bool addComma = false;
            JsonNode jsonNode2 = jsonNode.Children.First();

            switch (jsonNode2.TypeCategory)
            {
                case TypeCategorys.Basic:
                case TypeCategorys.Any:
                    foreach (var value in list)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        if (jsonNode2.ValueConverter.TryConvertTo(jsonNode.ObjectMemberInfo, jsonNode2.Type, obj, value, out jsonValue))
                        {
                            if (jsonNode2.ValueConverter.HasAddDoubleQuotes)
                            {
                                sb.Append(JsonConstants.DOUBLE_QUOTES);
                                sb.Append(jsonValue);
                                sb.Append(JsonConstants.DOUBLE_QUOTES);
                            }
                            else
                            {
                                sb.Append(jsonValue);
                            }
                        }
                        else
                        {
                            throw new ArgumentException($"类型{jsonNode.Type.DeclaringType.FullName}中的值\"{value}\"转换为json值失败");
                        }

                        addComma = true;
                    }
                    break;
                case TypeCategorys.Object:
                    foreach (var value in list)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }

                        this.WriteJsonObject(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                case TypeCategorys.IList:
                    foreach (var value in list)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }
                        this.WriteJsonIList(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                case TypeCategorys.IDictionary:
                    foreach (var value in list)
                    {
                        if (addComma)
                        {
                            //添加逗号
                            sb.Append(JsonConstants.COMMA_MARK);
                        }
                        this.WriteJsonIDictionary(sb, jsonNode2, value);
                        addComma = true;
                    }
                    break;
                default:
                    throw new NotImplementedException(jsonNode2.TypeCategory.ToString());
            }

            sb.Append(JsonConstants.RIGHT_BRCKET);
        }

        private void WriteJsonObject(StringBuilder sb, JsonNode jsonNode, object obj)
        {
            if (jsonNode.Children == null || jsonNode.Children.Count == 0)
            {
                if (obj == null)
                {
                    sb.Append("null");
                }
                else
                {
                    sb.Append('"');
                    sb.Append(obj);
                    sb.Append('"');
                }
                return;
            }

            sb.Append(JsonConstants.LEFT_BRACE);
            object value;
            bool addComma = false;
            string description;

            foreach (var childNode in jsonNode.Children)
            {
                if (addComma)
                {
                    //添加逗号
                    sb.Append(JsonConstants.COMMA_MARK);
                    addComma = false;
                }

                if (this._setting.WriteDescription)
                {
                    description = childNode.Description;
                    if (description != null)
                    {
                        if (description.Contains('\n'))
                        {
                            //有换行符，用/* ... */ 注释
                            sb.Append("/*");
                            sb.Append(description);
                            sb.Append("*/");
                        }
                        else
                        {
                            //无换行符，用// ... 注释
                            sb.Append("//");
                            sb.AppendLine(description);
                        }

                        continue;
                    }
                }

                value = childNode.GetValue(obj);
                if (childNode.MemberIgnoreer != null && childNode.MemberIgnoreer.Ignore(childNode.ObjectMemberInfo, obj, value))
                {
                    continue;
                }

                if (value == null && this._setting.IgnoreNullValues)
                {
                    continue;
                }

                addComma = true;
                if (value == null)
                {
                    this.WriteBasic(sb, childNode.JsonMemberName, null, false);
                    continue;
                }

                if (childNode.TypeCategory == TypeCategorys.Basic ||
                    childNode.TypeCategory == TypeCategorys.Any)
                {
                    if (childNode.ValueConverter != null)
                    {
                        this.WriteConverter(sb, childNode, obj, value);
                    }
                    else
                    {
                        this.WriteBasic(sb, childNode.JsonMemberName, Convert.ToString(value), false);
                    }
                    continue;
                }

                //名称
                sb.Append(JsonConstants.DOUBLE_QUOTES);
                sb.Append(childNode.JsonMemberName);
                sb.Append(JsonConstants.DOUBLE_QUOTES);

                //冒号
                sb.Append(JsonConstants.COLON_MARK);

                switch (childNode.TypeCategory)
                {
                    case TypeCategorys.Basic:
                        throw new ApplicationException();
                    case TypeCategorys.Object:
                        //值
                        this.WriteJsonObject(sb, childNode, value);
                        break;
                    case TypeCategorys.IList:
                        //值
                        this.WriteJsonIList(sb, childNode, value);
                        break;
                    case TypeCategorys.IDictionary:
                        //值
                        this.WriteJsonIDictionary(sb, childNode, value);
                        break;
                    default:
                        throw new NotImplementedException(childNode.TypeCategory.ToString());
                }
            }

            sb.Append(JsonConstants.RIGHT_BRACE);
        }

        private void WriteConverter(StringBuilder sb, JsonNode jsonNode, object obj, object value)
        {
            string jsonValue;
            if (jsonNode.ValueConverter.TryConvertTo(jsonNode.ObjectMemberInfo, jsonNode.Type, obj, value, out jsonValue))
            {
                this.WriteBasic(sb, jsonNode.JsonMemberName, jsonValue, jsonNode.ValueConverter.HasAddDoubleQuotes);
            }
            else
            {
                throw new ArgumentException($"成员{jsonNode.Type.FullName}.{jsonNode.ObjectMemberInfo.Name}对象值\"{value}\"转换为json值失败");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sb"></param>
        /// <param name="jsonMemberName"></param>
        /// <param name="jsonValue"></param>
        /// <param name="valueAddDoubleQuotes">true:值添加双引号;false:值不添加双引号</param>
        private void WriteBasic(StringBuilder sb, string jsonMemberName, string jsonValue, bool valueAddDoubleQuotes)
        {
            //名称
            sb.Append(JsonConstants.DOUBLE_QUOTES);
            sb.Append(jsonMemberName);
            sb.Append(JsonConstants.DOUBLE_QUOTES);

            //冒号
            sb.Append(JsonConstants.COLON_MARK);

            //值
            if (jsonValue == null)
            {
                sb.Append(JsonConstants.NULL);
            }
            else
            {
                string value2 = JsonHelper.Escape(jsonValue);
                if (valueAddDoubleQuotes)
                {
                    sb.Append(JsonConstants.DOUBLE_QUOTES);
                    sb.Append(value2);
                    sb.Append(JsonConstants.DOUBLE_QUOTES);
                }
                else
                {
                    sb.Append(value2);
                }
            }
        }
    }
}
